package com.dgut.courses_selecting_sys.controller;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.dgut.courses_selecting_sys.constants.CourseStatus;
import com.dgut.courses_selecting_sys.constants.RabbitMqKey;
import com.dgut.courses_selecting_sys.constants.RedisKey;
import com.dgut.courses_selecting_sys.constants.UserConstants;
import com.dgut.courses_selecting_sys.entity.*;
import com.dgut.courses_selecting_sys.enums.ExportEnum;
import com.dgut.courses_selecting_sys.service.ICourseService;
import com.dgut.courses_selecting_sys.service.IDeptService;
import com.dgut.courses_selecting_sys.service.IUserCourseService;
import com.dgut.courses_selecting_sys.service.IUserRoleService;
import com.dgut.courses_selecting_sys.utils.ExcelUtils;
import com.dgut.courses_selecting_sys.utils.RedisUtil;
import com.dgut.courses_selecting_sys.utils.ShiroUtils;
import com.dgut.courses_selecting_sys.utils.core.StrFormatter;
import com.dgut.courses_selecting_sys.vo.CourseVo;
import com.dgut.courses_selecting_sys.vo.PageVo;
import com.dgut.courses_selecting_sys.vo.ReviewAndConfirmVo;
import com.dgut.courses_selecting_sys.vo.UserCourseListVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.dgut.courses_selecting_sys.constants.RedisKey.USER_CHOOSE_COURSE_ID;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author yuec
 * @since 2020-08-14
 */
@Slf4j
@Controller
@RequestMapping("//course")
public class CourseController {

    @Autowired
    ICourseService courseService;
    @Autowired
    IDeptService deptService;
    @Autowired
    IUserRoleService userRoleService;
    @Autowired
    IUserCourseService userCourseService;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 返回创建课程的HTML的页面
     *
     * @return
     */
    @GetMapping("/create")
    public String create() {

        return "course/create";
    }


    /**
     * 返回修改课程的HTML的页面
     *
     * @return
     */
    @RequiresRoles(value = {"teacher", "admin"}, logical = Logical.OR)
    @GetMapping("/edit")
    public String edit(Model model, @RequestParam("courseId") int courseId) {
        Course course = courseService.getById(courseId);
        model.addAttribute("course", course);
        if (!course.getCreateBy().equals(ShiroUtils.getSysUser().getUserId())) {
            model.addAttribute("Authority", "-(无权限修改)");
        }
        return "course/edit";
    }

    /**
     * 返回课程选报情况
     *
     * @return
     */
    @RequiresRoles(value = {"teacher", "admin"}, logical = Logical.OR)
    @GetMapping("/chooseList")
    public String chooseList(Model model, @RequestParam("courseId") int courseId) {
        List<UserCourseListVo> userCourseList = userCourseService.listByCourseId(courseId);
        model.addAttribute("userCourseList", userCourseList);
        Course course = courseService.getById(courseId);
        model.addAttribute("courseName", course.getCourseName());
        model.addAttribute("count",course.getCount());
        model.addAttribute("chooseCount",userCourseList.size());
        return "course/chooseList";
    }


    @RequiresRoles(value = {"teacher", "admin"}, logical = Logical.OR)
    @ResponseBody
    @GetMapping("/editCourse")
    public AjaxResult editCourse(@RequestParam("courseId") int courseId) {
        UserRole userRole = (UserRole) userRoleService.getDataByUserId(ShiroUtils.getSysUser().getUserId());
        Course course = courseService.getById(courseId);
        if (userRole.getRoleId().equals( UserConstants.TEACHER) && Integer.valueOf(course.getStatus()) > Integer.valueOf(CourseStatus.REVIEW)) {
            return AjaxResult.error("课程已通过审核，无法修改\n请联系教务管理员！");
        } else if (!course.getCreateBy().equals(ShiroUtils.getSysUser().getUserId())) {
            return AjaxResult.error("课程非自己创建，无法修改");
        } else {
            return AjaxResult.success();
        }
    }

    @GetMapping("/history")
    public String history(Model model) {
        model.addAttribute("deptList", deptService.getDeptData());
        return "course/history";
    }

    @GetMapping("/choose")
    public String choose(ModelMap modelMap) throws Exception {
        User user = ShiroUtils.getSysUser();
        //列出审核完毕and可选课程
        List<Course> courseList = courseService.courseForChoose(user);
        log.info(courseList.toString());
        modelMap.addAttribute("courseList", courseList);
        return "course/choose";
    }

    @GetMapping("/myLog")
    public String myLog() {
        return "course/myLog";
    }

    /**
     * 返回用户所选的课程和报的活动
     * 并提供退选退报接口
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/myLogCourseData")
    public AjaxResult myLogData() throws Exception {
        User user = ShiroUtils.getSysUser();
        List courseIdList = courseService.checkUserCourseForChoose(user);
        if (courseIdList.size() == 0) {
            return AjaxResult.error("暂无记录");
        } else {
            List<Course> courseList = courseService.listByIds(courseIdList);
            for (Course course : courseList) {
                course.setTimetable(StrFormatter.toTimetable(course.getTimetable()));
            }
            return AjaxResult.success(courseList);
        }
    }


    @ResponseBody
    @GetMapping("/checkUserChoose")
    public AjaxResult checkUserChoose() {
        User user = ShiroUtils.getSysUser();
        //检查用户选择状态
        return AjaxResult.success(courseService.checkUserCourseForChoose(user));
    }


    /**
     * 选修课程
     * get请求考虑其幂等性
     *
     * @param courseId
     * @return
     */
    @ResponseBody
    @GetMapping("/chooseCourse")
    public AjaxResult chooseCourse(long courseId) {
        String keyNameForCount = RedisKey.COURSE_COUNT + courseId;
        if (redisUtil.hasKey(USER_CHOOSE_COURSE_ID.concat("" + courseId).concat(":" + ShiroUtils.getSysUser().getUserId()))) {
            return AjaxResult.error("你已选过该课程！");
        }
        Integer count = (Integer) redisUtil.get(keyNameForCount);
        if (count == null || count <= 0) {
            return AjaxResult.error("课程人数已被选满！");
        }
        RLock rLock = redissonClient.getLock(keyNameForCount);
        try {
            rLock.tryLock(10, TimeUnit.SECONDS);
            Integer count2 = (Integer) redisUtil.get(keyNameForCount);
            if (count2 == null || count2 <= 0) {
                return AjaxResult.error("课程人数已被选满！");
            } else {
                redisUtil.decr(keyNameForCount, 1L);
                Map<String, Long> map = new HashMap();
                map.put("userId", ShiroUtils.getSysUser().getUserId());
                map.put("courseId", courseId);
                rabbitTemplate.convertAndSend(RabbitMqKey.COURSE_USER_CONSUMER, JSON.toJSONString(map));
                return AjaxResult.success("课程选择成功！请留意邮件通知");
            }
        } catch (InterruptedException e) {
            //当redis中没有键的消息，则课程开选结束ttl过期，或者未开选和不存在该课程！
            return AjaxResult.error("课程不可选或未开选！");
        } finally {
            //Redis记录用户已选课
            redisUtil.set(USER_CHOOSE_COURSE_ID.concat("" + courseId).concat(":" + ShiroUtils.getSysUser().getUserId()), "");
            if(rLock.isLocked()){ // 是否还是锁定状态
                if(rLock.isHeldByCurrentThread()){ // 当前执行线程的锁
                    rLock.unlock(); // 释放锁
                }
            }
        }
    }


    /**
     * 取消选择1
     * 退选
     *
     * @param courseId
     * @return
     */
    @ResponseBody
    @PostMapping("/unChoose")
    public AjaxResult unChooseCourse(long courseId) {
        long userId = ShiroUtils.getSysUser().getUserId();
        Map<String, Object> map = new HashMap<>();
        map.put("user_id", userId);
        map.put("course_id", courseId);
        if (!courseService.getById(courseId).getStatus().equals(String.valueOf(CourseStatus.CHOICE))) {
            return AjaxResult.error("当前非课程开选时间");
        }
        if (redisUtil.hasKey(USER_CHOOSE_COURSE_ID.concat("" + courseId).concat(":" + ShiroUtils.getSysUser().getUserId()))) {
            if (userCourseService.removeByMap(map)) {
                redisUtil.del(USER_CHOOSE_COURSE_ID.concat("" + courseId).concat(":" + ShiroUtils.getSysUser().getUserId()));
                String keyNameForCount = RedisKey.COURSE_COUNT + courseId;
                redisUtil.incr(keyNameForCount, 1L);
                log.info("课程退选::退选人编号->{}->退选人->{}->退选课程编号->{}", userId, ShiroUtils.getSysUser().getUserName(), courseId);
                return AjaxResult.success("退选成功");
            }
            return AjaxResult.error("当前系统业务处理繁忙，请稍后再试！");
        }
        return AjaxResult.error("你尚未选取该门课程!");
    }

    /**
     * 获取历史/按条件获取结果集
     *
     * @param courseVo
     * @return
     */
    @ResponseBody
    @RequestMapping("/history/getData")
    public PageVo<Course> getData(CourseVo courseVo) throws Exception {
        UserRole userRole = userRoleService.getDataByUserId(ShiroUtils.getSysUser().getUserId());
        if (userRole.getRoleId().equals(UserConstants.STUDENT)) {
            courseVo.setStatus(CourseStatus.PASS);
        }
        return courseService.getDataByCourseVo(courseVo);
    }

    /**
     * 创建一个课程
     *
     * @param course
     * @return
     */
    @PostMapping("/create")
    @ResponseBody
    public AjaxResult createCourse(@Validated Course course) throws Exception {
        course.setCreateBy(ShiroUtils.getSysUser().getUserId());
        course.setCreateByName(ShiroUtils.getSysUser().getUserName());
        StrFormatter.toTimetable(course.getTimetable());
        if (courseService.save(course)) {
            log.info("创建课程::课程发起人->{}->课程名称->{}->课程编号->{}", course.getCreateByName(), course.getCreateByName(), course.getCourseId());
            return AjaxResult.success("课程成功创建");
        } else {
            return AjaxResult.error("课程创建出现错误!!!!");
        }
    }


    /**
     * 修改/编辑课程
     *
     * @param course
     * @return
     */
    @ResponseBody
    @PostMapping("/editCourse")
    public AjaxResult editCourse(Course course) {
        Course coursedb = courseService.getById(course.getCourseId());
        course.setCreateBy(coursedb.getCreateBy());
        course.setCreateByName(coursedb.getCreateByName());
        /**
         * 判断当前登录账号是否是课程创建是同个人，避免恶意修改
         */
        if (coursedb.getCreateBy().equals(ShiroUtils.getSysUser().getUserId()) && courseService.updateById(course)) {
            log.info("修改编辑课程::课程发起人->{}->课程名称->{}->课程编号->{}", course.getCreateByName(), course.getCreateByName(), course.getCourseId());
            return AjaxResult.success("修改成功！");
        } else {
            return AjaxResult.error("修改失败！");
        }
    }

    /**
     * 导出课程信息excel
     *
     * @param courseVo
     * @param response
     * @throws Exception
     */
    @PostMapping("/exportCourse")
    public void exportCourse(CourseVo courseVo, HttpServletResponse response) throws Exception {
        response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        courseVo.setPageSize(9999);
        courseVo.setPageNumber(0);
        PageVo<Course> pageVo = courseService.getDataByCourseVo(courseVo);
        try {
            ExcelUtils.writeExcel(response, pageVo.getRows(), Course.class, ExportEnum.COURSE);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * 审核课程，并设置开选课程的 选择时间-结束实际
     *
     * @param vo
     * @return
     */
    @ResponseBody
    @PostMapping("/reviewAndConfirm")
    public AjaxResult reviewAndConfirm(ReviewAndConfirmVo vo) throws Exception {
        String format = "yyyy-MM-dd'T'HH:mm";
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        Date startdate = sdf.parse(vo.getStartDate());
        Date enddate = sdf.parse(vo.getEndDate());
        long start = startdate.getTime();
        long end = enddate.getTime();
        long now = System.currentTimeMillis();
        if (start > end) {
            return AjaxResult.error("起始时间不可晚于结束时间!!!");
        }
        if (now > start) {
            return AjaxResult.error("起始时间不可晚于当前时间!!!");
        }
        List list = new ArrayList();
        list = (List) JSONArray.parse(vo.getCourses());
        if (list.size() == 0) {
            return AjaxResult.error("未选择需要核审的课程!!!");
        }
        if (courseService.reviewAndConfirm(start, end, now, list)) {
            log.info("审核课程成功::课程编号->{}->开选时间->{}->开选结束时间->{}", list, startdate, enddate);
            return AjaxResult.success("设置成功");
        }
        return AjaxResult.error("服务器设置开选时间出现问题!!!!");
    }
}
